import * as THREE from '../../build/three.module.js';

import { NURBSCurve } from '../../jsm/curves/NURBSCurve.js';
import { NURBSSurface } from '../../jsm/curves/NURBSSurface.js';

let windowHalfX = innerWidth / 2,
    mouseX = 0,
    mouseXOnMouseDown = 0,
    targetRotation = 0,
    targetRotationOnMouseDown = 0;

const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0);

const camera = new THREE.PerspectiveCamera(65, innerWidth / innerHeight, 1, 2000);
camera.position.set(0, 150, 750);

scene.add( new THREE.AmbientLight(0x888888) );

const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 1);
scene.add(light);

const group = new THREE.Group();
group.position.y = 50;
scene.add(group);

// 样条曲线
const nurbsControlPoints = [];
const nurbsDegree = 2;
const nurbsKnots = new Array(nurbsDegree + 1).fill(0);

for(let i = 0, j = 20; i < j; i += 1) {
  nurbsControlPoints.push(new THREE.Vector4(
    Math.random() * 400 - 200,
    Math.random() * 400,
    Math.random() * 400 - 200,
    1 // 控制点权重，越大影响力越大
  ))

  const knot = (i + 1) / (j - nurbsDegree);
  nurbsKnots.push(THREE.Math.clamp(knot, 0, 1)); 
}

const nurbsCurve = new NURBSCurve(nurbsDegree, nurbsKnots, nurbsControlPoints);

const nurbsGmt = new THREE.BufferGeometry();
nurbsGmt.setFromPoints( nurbsCurve.getPoints(200) );

const nurbsMtl = new THREE.LineBasicMaterial({ color: 0x333333 });

// 曲线
const nurbsLine = new THREE.Line(nurbsGmt, nurbsMtl);
nurbsLine.position.set(200, -100, 0);
group.add(nurbsLine);

// 控制点连线
const nurbsControlPointsGmt = new THREE.BufferGeometry();
nurbsControlPointsGmt.setFromPoints( nurbsCurve.controlPoints );
const nurbsControlPointsMtl = new THREE.LineBasicMaterial({ color: 0x333333, opacity: .25, transparent: true });
const nurbsControlPointsLine = new THREE.Line(nurbsControlPointsGmt, nurbsControlPointsMtl);
nurbsControlPointsLine.position.copy(nurbsLine.position);
group.add(nurbsControlPointsLine);

// 样条曲面
var nsControlPoints = [
  [
    new THREE.Vector4( - 200, - 200, 100, 1 ),
    new THREE.Vector4( - 200, - 100, - 200, 1 ),
    new THREE.Vector4( - 200, 100, 250, 1 ),
    new THREE.Vector4( - 200, 200, - 100, 1 )
  ],
  [
    new THREE.Vector4( 0, - 200, 0, 1 ),
    new THREE.Vector4( 0, - 100, - 100, 5 ),
    new THREE.Vector4( 0, 100, 150, 5 ),
    new THREE.Vector4( 0, 200, 0, 1 )
  ],
  [
    new THREE.Vector4( 200, - 200, - 100, 1 ),
    new THREE.Vector4( 200, - 100, 200, 1 ),
    new THREE.Vector4( 200, 100, - 250, 1 ),
    new THREE.Vector4( 200, 200, 100, 1 )
  ]
];
const degree1 = 2;
const degree2 = 3;
const knots1 = [0, 0, 0, 1, 1, 1];
const knots2 = [0, 0, 0, 0, 1, 1, 1, 1];
const nurbsSurface = new NURBSSurface(degree1, degree2, knots1, knots2, nsControlPoints);

const map = new THREE.TextureLoader().load('../../textures/uv_grid_opengl.jpg');
map.wrapS = map.wrapT = THREE.RepeatWrapping;
map.anisotropy = 16; // 16倍各向异性优化

const gmt = new THREE.ParametricBufferGeometry((u, v, target) => {
  return nurbsSurface.getPoint(u, v, target);
}, 20, 20);
const mtl = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide });
const mesh = new THREE.Mesh(gmt, mtl);
mesh.position.set(-200, 100, 0);
mesh.scale.multiplyScalar(1);
group.add(mesh);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(devicePixelRatio);
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

// 操作事件
const onDocumentMouseMove = e => {
  mouseX = e.clientX - windowHalfX;
  targetRotation = targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * .02;
}
const onDocumentMouseUp = () => {
  document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
  document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
  document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
}
const onDocumentMouseOut = () => {
  document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
  document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
  document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
}
const onDocumentMouseDown = e => {
  e.preventDefault();

  document.addEventListener('mousemove', onDocumentMouseMove, false);
  document.addEventListener('mouseup', onDocumentMouseUp, false);
  document.addEventListener('mouseout', onDocumentMouseOut, false);

  mouseXOnMouseDown = e.clientX - windowHalfX;
  targetRotationOnMouseDown = targetRotation;
}
document.addEventListener('mousedown', onDocumentMouseDown, false);

const render = () => {
  group.rotation.y += (targetRotation - group.rotation.y) * .05;
  renderer.render(scene, camera);
}

const animate = () => {
  requestAnimationFrame(animate);

  render();
}
animate();

const handleWindowResize = () => {
  windowHalfX = innerWidth / 2;

  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(innerWidth, innerHeight);
}
window.addEventListener('resize', handleWindowResize);